﻿namespace ClassyBlog.DataAccess.Specs.Unit
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using Machine.Specifications;
    using NSubstitute;

    using Infrastructure;

    [Subject(typeof(PathResolver))]
    public class when_creating_path_resolver_with_null_file_system
    {
        static Exception exception;

        Because of = () => exception =
            Catch.Exception(() => new PathResolver(null));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentNullException>();
    }

    public abstract class PathResolverSpec
    {
        protected const string DataPath =
            @"c:\inetpub\wwwroot\classyblog\app_data\pages";

        protected const string PathName = "2010-04-02-17-30-00_about-us.md";

        protected static IFileSystem fileSystem;
        protected static PathResolver pathResolver;

        Establish context = () =>
        {
            fileSystem = Substitute.For<IFileSystem>();
        };
    }

    [Subject(typeof(PathResolver))]
    public class when_resolving_path_with_blank_data_path : PathResolverSpec
    {
        static Exception exception;

        Establish context = () => pathResolver = new PathResolver(fileSystem);

        Because of = () => exception = 
            Catch.Exception(() =>
                pathResolver.Resolve(string.Empty, PathName));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentException>();
    }

    [Subject(typeof(PathResolver))]
    public class when_resolving_path_with_blank_relative_path :
        PathResolverSpec
    {
        static Exception exception;

        Establish context = () => pathResolver = new PathResolver(fileSystem);

        Because of = () => exception =
            Catch.Exception(() =>
                pathResolver.Resolve(DataPath, string.Empty));

        It should_throw_exception = () =>
            exception.ShouldBeOfType<ArgumentException>();
    }

    [Subject(typeof(PathResolver))]
    public class when_resolving_existent_path : PathResolverSpec
    {
        static string filePath;

        Establish context = () =>
        {
            fileSystem.Files(
                Arg.Any<string>(),
                Arg.Any<string>(),
                Arg.Any<bool>()).
                Returns(new[] { string.Join("\\", DataPath, PathName) });

            pathResolver = new PathResolver(fileSystem);
        };

        Because of = () =>
            filePath = pathResolver.Resolve(DataPath, "about-us");

        It should_return_the_extact_path = () =>
            filePath.ShouldEqual(string.Join("\\", DataPath, PathName));
    }

    [Subject(typeof(PathResolver))]
    public class when_resolving_non_existent_path : PathResolverSpec
    {
        static string filePath;

        Establish context = () =>
        {
            fileSystem.Files(Arg.Any<string>(), Arg.Any<string>(), Arg.Any<bool>()).Returns(new string[0]);

            pathResolver = new PathResolver(fileSystem);
        };

        Because of = () => filePath = pathResolver.Resolve(DataPath, "foo");

        It should_return_null = () => filePath.ShouldBeNull();
    }

    [Subject(typeof(PathResolver))]
    public class when_resolving_paths_with_blank_data_path : PathResolverSpec
    {
        static Exception exception;

        Establish context = () => pathResolver = new PathResolver(fileSystem);

        Because of = () => exception = Catch.Exception(() => pathResolver.ResolveAll(string.Empty, PathName));

        It should_throw_exception = () => exception.ShouldBeOfType<ArgumentException>();
    }

    [Subject(typeof(PathResolver))]
    public class when_resolving_existent_directory : PathResolverSpec
    {
        const string PathName1 = "2010-04-02-17-30-00_about-us.md";
        const string PathName2 = "2010-04-02-17-30-00_about-you.md";

        static IList<string> filePaths;

        Establish context = () =>
        {
            fileSystem.Files(
                Arg.Any<string>(),
                Arg.Any<string>(),
                Arg.Any<bool>()).
                Returns(new[]
                            {
                                string.Join("\\", DataPath, "help", PathName1),
                                string.Join("\\", DataPath, "help", PathName2)
                            });

            pathResolver = new PathResolver(fileSystem);
        };

        Because of = () =>
            filePaths = pathResolver.ResolveAll(DataPath, "help").ToList();

        It should_return_the_extact_paths = () =>
        {
            filePaths[0].ShouldEqual(string.Join(
                "\\", DataPath, "help", PathName1));

            filePaths[1].ShouldEqual(string.Join(
                "\\", DataPath, "help", PathName2));
        };
    }

    [Subject(typeof(PathResolver))]
    public class when_resolving_non_existent_directory : PathResolverSpec
    {
        static IEnumerable<string> filePaths;

        Establish context = () =>
        {
            fileSystem.Files(
                Arg.Any<string>(),
                Arg.Any<string>(),
                Arg.Any<bool>()).
                Returns(new string[0]);

            pathResolver = new PathResolver(fileSystem);
        };

        Because of = () =>
            filePaths = pathResolver.ResolveAll(DataPath, "foo");

        It should_return_empty_path_list = () => filePaths.ShouldBeEmpty();
    }

    [Subject(typeof(PathResolver))]
    public class when_resolving_existent_directory_which_contains_hidden_files :
        PathResolverSpec
    {
        const string PathName1 = "2010-04-02-17-30-00_about-us.md";
        const string PathName2 = "2010-04-02-17-30-00_about-you.md";
        const string PathName3 = ".2010-05-21-03-30-30_about-him.md";
        const string PathName4 = ".2010-05-21-03-30-40_about-her.md";

        static IList<string> filePaths;

        Establish context = () =>
        {
            var paths = new[]
                              {
                                string.Join("\\", DataPath, "help", PathName1),
                                string.Join("\\", DataPath, "help", PathName2),
                                string.Join("\\", DataPath, "help", PathName3),
                                string.Join("\\", DataPath, "help", PathName3)
                              }; 

            fileSystem.Files(
                Arg.Any<string>(),
                Arg.Any<string>(),
                Arg.Any<bool>()).
                Returns(paths);

            pathResolver = new PathResolver(fileSystem);
        };

        Because of = () =>
            filePaths = pathResolver.ResolveAll(DataPath, "help").ToList();

        It should_not_include_hidden_files = () =>
        {
            filePaths.ShouldNotContain(
                string.Join("\\", DataPath, "help", PathName3));

            filePaths.ShouldNotContain(
                string.Join("\\", DataPath, "help", PathName4));
        };
    }
}